#!/bin/sh

# This file defines the authentication token manager (atm).

# Copyright (C) 2016-2018 mlaine@sdfeu.org

# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:

# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.

# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

#-----------------------------------------------------------------------

atmd="$HOME/.atm"

chars='A-Za-z0-9!"#$%&()*+-/:;<=>?@[]^_{}' # token character set
tk_len=24 # token length

mktoken() {
    gid="${1%/*}"
    [ "$gid" != "$1" ] && ( mkdir -p "$atmd/$gid" || return 1 )
    if [ "$2" ]; then
        printf '%s' "$2" | gpg --yes --default-recipient-self -eo "$atmd/$1"
    else
    tr -cd "$chars" < /dev/urandom | dd bs="$tk_len" count=1 2>/dev/null\
        | gpg --yes --default-recipient-self -eo "$atmd/$1"
    fi
}

add() {
    if [ -d "$atmd/$1" ]; then
        printf 'Will not overwrite a group!\n'
        return 1
    elif [ -f "$atmd/$1" ]; then
        printf 'Token %s exists, overwrite? [y/N] ' "$1"
        read -r ans
        [ "$ans" != 'y' ] && return 0
    fi
    mktoken "$1" "$2" && printf 'Added token %s.\n' "$1"
}

del() {
    if [ -f "$atmd/$1" ]; then
        fmt1='Delete token %s? [y/N] '
        fmt2='Deleted token %s.\n'
    elif [ -d "$atmd/$1" ]; then
        fmt1='Delete %s and ALL contained tokens? [y/N] '
        fmt2='Deleted %s and all contained tokens.\n'
    else
        printf 'Token %s does not exist!\n' "$1"
        return 1
    fi
    printf "$fmt1" "$1"
    read -r ans
    [ "$ans" != 'y' ] && return 0
    rm -r "${atmd:?}/$1"
    printf "$fmt2" "$1"
}

mvt() {
    if [ -f "$atmd/$1" ]; then
        fmt1='Move token %s to %s? [y/N] '
        fmt2='Moved token %s to %s.\n'
    elif [ -d "$atmd/$1" ]; then
        fmt1='Move %s and ALL contained tokens to %s? [y/N] '
        fmt2='Moved %s and ALL contained tokens to %s.\n'
    else
        printf 'Token %s does not exist!\n' "$1"
        return 1
    fi
    printf "$fmt1" "$1" "$2"
    read -r ans
    [ "$ans" != 'y' ] && return 0
    if [ -f "$atmd/$2" ]; then
        if [ "${1##*/}" = "${2##*/}" ]; then
            token="$2"
        elif [ -e "$atmd/$2/${1##*/}" ]; then
            token="$2/${1##*/}"
        fi
        printf 'Token %s exists, overwrite? [y/N] ' "$token"
        read -r ans
        [ "$ans" != 'y' ] && return 0
    else
        gid="${2%/*}"
        [ "$gid" != "$2" ] && mkdir -p "$atmd/$gid"
    fi
    mv "${atmd:?}/$1" "$atmd/$2" || return 1
    printf "$fmt2" "$1" "$2"
}

ins() {
    if [ -f "$atmd/$1" ]; then
        gpg -dq --pinentry-mode loopback "$atmd/$1" | xsel -i
    else
        printf 'Token %s does not exist!\n' "$1"
    fi
}

prt() {
    if [ -f "$atmd/$1" ]; then
        printf 'Print token %s to standard output? [y/N] ' "$1"
        read -r ans
        if [ "$ans" = 'y' ]; then
            gpg -dq --pinentry-mode loopback "$atmd/$1"
            echo
        fi
    else
        printf 'Token %s does not exist!\n' "$1"
    fi
}

serv() {
    case "$1" in
        '')
            xsel -c 2>/dev/null && clear
            serv 'ls'
            serv '?' ;;
        'a'|'add')
            add "$2" "$3" ;;
        'd'|'del')
            del "$2" ;;
        'm'|'mvt')
            mvt "$2" "$3" ;;
        'i'|'ins')
            ins "$2" ;;
        'p'|'prt')
            prt "$2" ;;
        'l'|'ls')
            ([ "$2" = '-d' ] && tree -d "$atmd/$3") || tree "$atmd/$2" ;;
        'q')
            exit 0;;
        '?')
            printf 'Syntax: add|del|mvt|ins|prt|ls|q|? [gid/token [value]]\n'
            ;;
    esac
}

main() {
    umask 0077
    trap "printf '\n'; continue" 2
    trap 'xsel -c 2>/dev/null; exit' 0 1 15
    [ ! -d "$atmd" ] && mkdir -p "$atmd"
    serv 'ls'
    serv '?'
    while true; do
        printf 'Command: '
        read -r cmd token value
        serv "$cmd" "$token" "$value"
    done
}

main "$@"

